import random  
import math  
from tkinter import *  
  
CANVAS_WIDTH = 640  
CANVAS_HEIGHT = 480  
CANVAS_CENTER_X = CANVAS_WIDTH / 2  
CANVAS_CENTER_Y = CANVAS_HEIGHT / 2  
HEART_COLOR = "#ff2121"  
  
def heart_function(t, shrink_ratio=11):  
    x = 16 * math.sin(t) ** 3  
    y = -(13 * math.cos(t) - 5 * math.cos(2 * t) - 2 * math.cos(3 * t) - math.cos(4 * t))  
    x *= shrink_ratio  
    y *= shrink_ratio  
    x += CANVAS_CENTER_X  
    y += CANVAS_CENTER_Y  
    return int(x), int(y)  
  
def scatter_inside(x, y, beta=0.15):  
    ratio_x = -beta * math.log(random.random())  
    ratio_y = -beta * math.log(random.random())  
    dx = ratio_x * (x - CANVAS_CENTER_X) / CANVAS_CENTER_X  
    dy = ratio_y * (y - CANVAS_CENTER_Y) / CANVAS_CENTER_Y  
    return int(x - dx), int(y - dy)  
  
class Heart:  
    def __init__(self, generate_frame=20):  
        self._points = set()  
        self._diffusion_points = set()  
        self.all_points = {}  
        self.build(2000)  
        self.generate_frame = generate_frame  
        for frame in range(generate_frame):  
            self.calc(frame)  
  
    def build(self, number):  
        for _ in range(number):  
            t = random.uniform(0, 2 * math.pi)  
            x, y = heart_function(t)  
            self._points.add((x, y))  
        for x, y in list(self._points):  
            for _ in range(3):  
                x, y = scatter_inside(x, y, 0.05)  
                self._diffusion_points.add((x, y))  
  
    def calc(self, generate_frame):  
        ratio = 10 * math.sin(generate_frame / 10 * math.pi)  
        all_points = []  
        for _ in range(1000):  # 简化处理，减少计算量  
            t = random.uniform(0, 2 * math.pi)  
            x, y = heart_function(t, shrink_ratio=11.6)  
            x, y = scatter_inside(x, y, 0.1 * ratio)  
            size = random.choice((1, 2, 3))  
            all_points.append((x, y, size))  
        for x, y in self._points:  
            x, y = scatter_inside(x, y, ratio)  
            size = random.randint(1, 3)  
            all_points.append((x, y, size))  
        for x, y in self._diffusion_points:  
            x, y = scatter_inside(x, y, ratio)  
            size = random.randint(1, 2)  
            all_points.append((x, y, size))  
        self.all_points[generate_frame] = all_points  
  
    def render(self, render_canvas, render_frame):  
        for x, y, size in self.all_points[render_frame % self.generate_frame]:  
            render_canvas.create_rectangle(x, y, x + size, y + size, width=0, fill=HEART_COLOR)  
  
def draw(main, render_canvas, render_heart, render_frame=0):  
    render_canvas.delete('all')  
    render_heart.render(render_canvas, render_frame)  
    main.after(160, draw, main, render_canvas, render_heart, render_frame + 1)  
  
if __name__ == '__main__':  
    root = Tk()  
    canvas = Canvas(root, bg='black', height=CANVAS_HEIGHT, width=CANVAS_WIDTH)  
    canvas.pack()  
    heart = Heart()  
    draw(root, canvas, heart)  
    root.mainloop()